﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;

namespace NPlurk
{
    public class RequestBase
    {
        #region Properties

        public CookieContainer Cookies { get; private set; }
        public string PlurkKey { get; private set; }
        public string Lang { get; set; }

        protected JavaScriptSerializer _jss = new JavaScriptSerializer();

        #endregion

        #region Constructor

        public RequestBase(string plurkKey)
        {
            Cookies = new CookieContainer();
            PlurkKey = plurkKey;
            Lang = "en";
        }

        #endregion

        protected NameValueCollection ComposeParam(object param)
        {
            var nvc = new NameValueCollection { { "api_key", PlurkKey } };
            if (param != null)
            {
                Type t = param.GetType();
                foreach (PropertyInfo property in t.GetProperties())
                {
                    nvc[property.Name] = property.GetValue(param, null).ToString();
                }
            }
            return nvc;
        }

        protected T RequestObject<T>(string uri, string method, NameValueCollection nvc)
        {
            string response = Request(uri, method, nvc);

            return _jss.Deserialize<T>(response);
        }

        protected string Request(string uri, string method, NameValueCollection nvc)
        {
            HttpWebRequest request;

            switch (method)
            {
                case "POST":
                    request = PrepareHttpPost(uri, nvc);
                    break;
                default:
                    request = PrepareHttpGet(uri, nvc);
                    break;
            }

            try
            {
                WebResponse response = request.GetResponse();
                Stream responseStream = response.GetResponseStream();
                var responseReader = new StreamReader(responseStream);
                string responseString = responseReader.ReadToEnd();
                return responseString;
            }
            catch (WebException ex)
            {
                if (ex.Response == null) throw ex;

                var exceptionStreamReader = new StreamReader(ex.Response.GetResponseStream());
                string exceptionJson = exceptionStreamReader.ReadToEnd();

                var plurkError = _jss.Deserialize<PlurkError>(exceptionJson);

                var plurkException = new PlurkException(plurkError.error_text, plurkError, ex);
                throw plurkException;
            }
        }

        protected HttpWebRequest PrepareHttpPost(string uri, NameValueCollection nvc)
        {
            byte[] body = MakeBody(nvc);

            var request = (HttpWebRequest)WebRequest.Create(uri);
            request.CookieContainer = Cookies;
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = body.Length;
            Stream stream = request.GetRequestStream();
            stream.Write(body, 0, body.Length);
            stream.Close();
            return request;
        }

        protected HttpWebRequest PrepareHttpGet(string uri, NameValueCollection nvc)
        {
            var sb = new StringBuilder();
            string prefix = uri.IndexOf("?") == -1 ? "?" : "&";
            sb.Append(uri);
            foreach (string key in nvc.AllKeys)
            {
                sb.Append(prefix).Append(key).Append("=").Append(HttpUtility.UrlDecode(nvc[key]));
                prefix = "&";
            }
            var request = (HttpWebRequest)WebRequest.Create(sb.ToString());
            request.CookieContainer = Cookies;
            request.Method = "GET";
            return request;
        }

        protected static byte[] MakeBody(NameValueCollection nvc)
        {
            var sb = new StringBuilder();
            string prefix = "";
            foreach (string key in nvc.AllKeys)
            {
                sb.Append(prefix).Append(key).Append("=").Append(HttpUtility.UrlDecode(nvc[key]));
                prefix = "&";
            }
            return Encoding.UTF8.GetBytes(sb.ToString());
        }

    }
}
